/*----------------------------------------------------------------------------
 *      RL-ARM - RTX
 *----------------------------------------------------------------------------
 *      Name:    DASHBOARD_TEMPLATE.C
 *      Purpose: RTX example program
 *----------------------------------------------------------------------------
 *      This code has been modified from the example Blinky project of the
                RealView Run-Time Library.
 *      Copyright (c) 2004-2011 KEIL - An ARM Company. All rights reserved.
 *---------------------------------------------------------------------------*/

#include <RTL.h>
#include <91x_lib.H>
#include "LCD.h"
#include <stdio.h>
#include <stdlib.h>

#ifndef POTEN_MAX
#define POTEN_MAX 1024                    /* Maximum value for potentiometer */
#endif

#ifndef LIGHT_LEVEL
#define LIGHT_LEVEL 5
#endif

#ifndef SPEED_LIMIT
#define SPEED_LIMIT 280
#endif

typedef enum { DOOR_CLOSED, DOOR_OPEN } DOOR_State;
typedef enum { ENGINE_ON, ENGINE_OFF } ENGINE_State;
DOOR_State door_state;
ENGINE_State engine_state;

extern __irq void ADC_IRQ_Handler (void); /* ADC  interrupt routine          */
extern unsigned char AD_in_progress;      /* AD conversion in progress flag  */
short potentiometer;
int duty_cycle = LIGHT_LEVEL;
short slider;
int counter = 0;
int blink_counter = 0;
int alarm = 0;

os_mbx_declare(p_box, 20); // potentiometer mailbox
os_mbx_declare(s_box, 20); // slider mailbox

// extern OS_MBX p_box;
// extern OS_MBX s_box;

OS_TID adc_conv_id;
OS_TID duty_cycle_id;
OS_TID light_control_id;
OS_TID lcd_control_id;
OS_TID speed_control_id;
OS_TID alarm_control_id;
OS_TID engine_door_contorl_id;
OS_MUT mutex1;

unsigned char A0,A1; //A0 - Button 3.5, A1 - Button 3.6
unsigned char B0 = 0,B1 = 0,B2 = 0,B3 = 0,B4 = 0,B5 = 0,B6 = 0,B7 = 0;
// B0-B7 represent LED's 0 through 7

// Function to read input
void read_buttons() {
    // BUTTON_3_5:
    A0 = !(GPIO3->DR[0x080]>>5); // Returns 1 when pressed and 0 when released
    // BUTTON_3_6:
    A1 = !(GPIO3->DR[0x100]>>6); // Returns 1 when pressed and 0 when released

}

void start_ADC () {
    if (!AD_in_progress) {             /* If conversion not in progress      */
        AD_in_progress = 1;            /* Flag that AD conversion is started */
        ADC->CR |= 0x0423;             /* Set STR bit (Start Conversion)     */
    }
    // Now the interrupt will be called when conversion ends
}

// Function to write to led
void write_led() {
    unsigned char mask = 0;

    mask  = (B0<<0);
    mask |= (B1<<1);
    mask |= (B2<<2);
    mask |= (B3<<3);
    mask |= (B4<<4);
    mask |= (B5<<5);
    mask |= (B6<<6);
    mask |= (B7<<7);

    GPIO7->DR[0x3FC] = mask;
}

void set_lights(int num) {
    B0 = num;
    B1 = num;
    B2 = num;
    B3 = num;
    B4 = num;
    B5 = num;
}

void write_lcd() {
    char speed[15];
    char light[15];
    os_mut_wait(&mutex1, 10);
    sprintf(speed, "SPEED:%d DR:%s", slider,
        (door_state == DOOR_OPEN ? "OP" : "CL"));
    sprintf(light, "LIGHT:%d EG:%s", LIGHT_LEVEL - duty_cycle + 1,
        (engine_state == ENGINE_ON ? "ON" : "OFF"));
    LCD_cls();
    LCD_puts((U8 *) speed);
    LCD_gotoxy(1,2);
    LCD_puts((U8 *) light);
    LCD_cur_off();
    os_mut_release(&mutex1);
}

void blink_led() {
    if (blink_counter % 2 == 0) {
        B6 = 1;
        B7 = 1;
    } else {
        B6 = 0;
        B7 = 0;
    }
    blink_counter++;
    if (blink_counter == 2) {
        blink_counter = 0;
    }
}

void check_alarm() {
    if (slider > SPEED_LIMIT) {
        alarm = 1;
    } else if (engine_state == ENGINE_ON && door_state == DOOR_OPEN) {
        alarm = 1;
    } else {
        alarm = 0;
    }
}

/*----------------------------------------------------------------------------
 *        Task 1 'ADC_Con': ADC Conversion
 *---------------------------------------------------------------------------*/
__task void ADC_Con(void) {
    // timing
    const unsigned int period = 200;
    os_mbx_init(&p_box, sizeof(p_box));
    os_mbx_init(&s_box, sizeof(s_box));
    os_itv_set(period);
    for(;;) { 
        os_itv_wait();
        /* Do actions below */
        start_ADC();
    }
} // End ADC_Con(void)

/*----------------------------------------------------------------------------
 *        Task 2 'Duty_Cycle_Comp': Duty Cycle Computation
 *---------------------------------------------------------------------------*/
__task void Duty_Cycle_Comp(void) {
    void *p_msg;
    // void *s_msg;
    // const unsigned int period = 200;
    // os_itv_set(period);
    for (;;) {
        // os_itv_wait();
        if (isr_mbx_receive (&p_box, &p_msg) == OS_R_MBX) {
            potentiometer = *(short *)p_msg;
            if (engine_state == ENGINE_ON) {
                slider = *((short *)p_msg + 1);
            } else {
                slider = 0;
            }
            duty_cycle = potentiometer * LIGHT_LEVEL / POTEN_MAX + 1;
            free(p_msg);
        }
    }
}

/*----------------------------------------------------------------------------
 *        Task 3 'Light_Control': Light Level Controller
 *---------------------------------------------------------------------------*/
__task void Light_Control(void) {
    const unsigned int period = 2;
    os_itv_set(period);
    for (;;) {
        if (counter < duty_cycle * 2 - 1) {
            set_lights(0);
        } else {
            set_lights(1);
        }
        write_led();
        counter++;
        if (counter >= LIGHT_LEVEL * 2) {
            counter = 0;
        }
        os_itv_wait();
    }
}

/*----------------------------------------------------------------------------
 *        Task 4 'LCD_Control': Light Level on LCD
 *---------------------------------------------------------------------------*/
__task void LCD_Control(void) {
    const unsigned int period = 200;
    os_itv_set(period);
    for (;;) {
        os_itv_wait();
        write_lcd();
    }
}

/*----------------------------------------------------------------------------
 *        Task 5 'Speed_Control': Speed on LCD, reach speed limit
 *---------------------------------------------------------------------------*/
__task void Speed_Control(void) {
    const unsigned int period = 200;
    os_itv_set(period);
    for (;;) {
        write_lcd();
        os_itv_wait();
        check_alarm();
        // if (slider > SPEED_LIMIT) {
        //     alarm = 1;
        // } else {
        //     alarm = 0;
        // }
    }
}

/*----------------------------------------------------------------------------
 *        Task 6 'Alarm_Control': Fire the alarm when car reach speed limit
 *---------------------------------------------------------------------------*/
__task void Alarm_Control(void) {
    const unsigned int period = 400;
    os_itv_set(period);
    for (;;) {
        os_itv_wait();
        if (alarm) {
            blink_led();
        } else {
            B6 = 0;
            B7 = 0;
        }
    }
}

/*----------------------------------------------------------------------------
 *        Task 6 'Alarm_Control': Fire the alarm when car reach speed limit
 *---------------------------------------------------------------------------*/
__task void Engine_Door_Control(void) {
    const unsigned int period = 200;
    os_itv_set(period);
    for (;;) {
        os_itv_wait();
        read_buttons();
        switch (door_state) {
            case DOOR_CLOSED:
                if (A0) {
                    door_state = DOOR_OPEN;
                }
                break;
            case DOOR_OPEN:
                if (A0) {
                    door_state = DOOR_CLOSED;
                }
                break;
            default:
                door_state = DOOR_CLOSED;
        }
        switch (engine_state) {
            case ENGINE_OFF:
                if (A1) {
                    engine_state = ENGINE_ON;
                }
                break;
            case ENGINE_ON:
                if (A1) {
                    engine_state = ENGINE_OFF;
                }
                break;
            default:
                engine_state = ENGINE_ON;
        }
        check_alarm();
    }
}


/*----------------------------------------------------------------------------
 *        Task 0 'init': Initialize
 *---------------------------------------------------------------------------*/
__task void init(void) {

    unsigned int n = 0;
          
    /* Set up Potentiometer and Light sensor */

    SCU->GPIOIN[4]  |= 0x07;             /* P4.0, P4.1 and P4.2 input - mode 0 */
    SCU->GPIOOUT[4] &= 0xFFC0;           /* P4.0 output - mode 0             */

    /* To look up search for GPIO4_DIR in the peripherals manual             */
    GPIO4->DDR      &= 0xF8;             /* P4.0 and P4.1 direction - input  */
    SCU->GPIOANA    |= 0x0007;           /* P4.0 analog mode ON              */

    /* To understand the ADC configuration register consult the manual       */
    ADC->CR         |= 0x0042;           /* Set POR bit                      */
    for (n = 0; n < 100000; n ++);       /* Wait > 1 ms  (at 96 MHz)         */

    ADC->CR         &= 0xFFB7;           /* Clear STB bit                    */
    for (n = 0; n < 1500; n ++);         /* Wait > 15 us (at 96 MHz)         */

    ADC->CR         |= 0x0423;           /* Enable end of conversion interupt*/
    ADC->CCR         = 0x003F; /* AD Conversion for Channels 0,1,2, No WDG on Ch 0 */

    /* Configure and enable IRQ for A/D Converter (ADC)                      */
    VIC0->VAiR[15]  = (unsigned int)ADC_IRQ_Handler; /* Setup ADC IRQ Hndl addr */
    VIC0->VCiR[15] |= 0x20;              /* Enable the vector interrupt      */
    VIC0->VCiR[15] |= 15;                /* Specify the interrupt number     */
    VIC0->INTER    |= (1<<15);           /* Enable ADC interrupt             */

    /* Configuring LED                     */
    SCU->GPIOOUT[7]  = 0x5555;
    GPIO7->DDR       = 0xFF;
    GPIO7->DR[0x3FC] = 0x00;

    /* LCD Setup                           */
    GPIO8->DDR       = 0xFF;
    GPIO9->DDR       = 0x07;

    /* Port 3 setup for button 3.5 and 3.6 */
    SCU->GPIOIN[3]  |= 0x60;
    SCU->GPIOOUT[3] &= 0xC3FF;
    GPIO3->DDR      &= 0x9F;

    LCD_init(); //Initialize LCD
    LCD_cur_off(); //Remove LCD cursor
    LCD_cls(); //Clearing LCD screen

    os_mut_init(mutex1);

    light_control_id = os_tsk_create(Light_Control, 2);
    lcd_control_id = os_tsk_create(LCD_Control, 1);
    speed_control_id = os_tsk_create(Speed_Control, 2);
    alarm_control_id = os_tsk_create(Alarm_Control, 1);
    engine_door_contorl_id = os_tsk_create(Engine_Door_Control, 1);
    adc_conv_id = os_tsk_create(ADC_Con, 2);
    duty_cycle_id = os_tsk_create(Duty_Cycle_Comp, 1);
    os_tsk_delete_self ();
}


/*----------------------------------------------------------------------------
 *        Main: Initialize and start RTX Kernel
 *---------------------------------------------------------------------------*/
int main(void) {
    os_sys_init(init);                   /* Initialize RTX and start init    */
    return 0;
}

/*----------------------------------------------------------------------------
 * end of file
 *---------------------------------------------------------------------------*/
